﻿/*
* Copyright (c) 2011, Johnson County Community College (JCCC)
* All rights reserved.
* BSD License: http://www.opensource.org/licenses/bsd-license.php
* 
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
*    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 
*      in the documentation and/or other materials provided with the distribution.
*    * Neither the name of JCCC nor the names of its contributors may be used to endorse or promote products derived 
*      from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections.Generic;



    /// <summary>
    /// Wall = 0 or 1
    /// Floor = 2
    /// </summary>
    public class FourRoomGenerator : IDungeonMapGenerator
    {
        Dungeon dungeon;
        Dijkstra2DMap dijMap;
		

        /// <summary>
        /// border around the edge of the map that can't have rooms.  Default is 4
        /// </summary>
        public int safeArea;

        /// <summary>
        /// How wide to generate the map.  Default is 35
        /// </summary>
        public int dungeonWidth;
        /// <summary>
        /// How high to generate the map.  Default is 35
        /// </summary>
        public int dungeonHeight;

        /// <summary>
        /// Number of Rooms = dungeonWidth / targetScale.  Do not set below default of 2.5f
        /// </summary>
        public float targetScale;

        /// <summary>
        /// How much space around a room.  Do not set below 2.  Default is 3
        /// </summary>
        public int roomPadding;

        /// <summary>
        /// Minimum room height
        /// </summary>
        public int minRoomHeight;
        /// <summary>
        /// Maximum room height
        /// </summary>
        public int maxRoomHeight;
        /// <summary>
        /// Minimum room Width
        /// </summary>
        public int minRoomWidth;
        /// <summary>
        /// Maximum room Width
        /// </summary>
        public int maxRoomWidth;
	
		
	
	
        public FourRoomGenerator()
        {
		
            safeArea = 4;
            dungeonHeight = 55;
            dungeonWidth = 55;
            targetScale = 2.5f;
            roomPadding = 3;
            minRoomHeight = 2;
            minRoomWidth = 2;
            maxRoomHeight = 6;
            maxRoomWidth = 6;
            //EqatecMonitor.SendFeature("Rogue.FourRoomGenerator");
        }

        public void GenerateMap(Dungeon dungeon)
        {
            //EqatecMonitor.StartFeatureTime("Rogue.FourRoomDungeonGenerate");
            DungeonRoom tempRoom;

            this.dungeon = dungeon;

            Random rnd = new Random();

            dungeon.rooms.Clear();
            //change the x,y if you want to change the size of the dungeon
            dungeon.SetDungeonsize(dungeonWidth, dungeonHeight);

            //randomize walls
            //assumes using the sample tilemap
            for (int i = 0; i < dungeon.tileMap.Length; i++) dungeon.tileMap[i] = rnd.Next(0, 3);

            int count = 0;

            //never change targetScale to less than 2.5f.
            //it will take a looooooong time if you do
            targetScale = 2.5f;
            int targetRooms = (int)(dungeon.dungeonWidth / targetScale);
            if (targetRooms % 2 != 0) targetRooms += 1; //even number of rooms just because
            //targetRooms = 4;

            while (dungeon.rooms.Count < targetRooms)
            {
                count++;
                if (count > 1000)       //never went above 300 when logging
                {
                    dungeon.rooms.Clear();  //freeze protection
                    count = 0;
                    //if(dungeon.log != null) dungeon.log.WriteDebug("Generation: over 1,000 fails");
                }
                tempRoom = new DungeonRoom();
                Rectangle tempRectangle;
                tempRectangle.X = rnd.Next(safeArea + 1, dungeon.dungeonWidth - safeArea - maxRoomHeight);
                tempRectangle.Y = rnd.Next(safeArea + 1, dungeon.dungeonHeight - safeArea - maxRoomHeight);
                tempRectangle.Width = rnd.Next(minRoomWidth, maxRoomWidth + 1);
                tempRectangle.Height = rnd.Next(minRoomHeight, maxRoomHeight + 1);
                //do not increase padding beyond three unless you decrease the number of rooms by increasing targetScale
                //tempRoom.setRoom(new Rectangle(rnd.Next(safeArea + 1, dungeon.dungeonWidth - safeArea - maxRoomHeight), rnd.Next(safeArea + 1, dungeon.dungeonHeight - safeArea - maxRoomHeight), rnd.Next(minRoomWidth, maxRoomWidth + 1), rnd.Next(minRoomHeight, maxRoomHeight + 1)), roomPadding);
                tempRoom.setRoom(tempRectangle, roomPadding);
                bool collide = false;
                for (int i = 0; i < dungeon.rooms.Count; i++)
                {
                    if (dungeon.rooms[i].paddedArea.Intersects(tempRoom.paddedArea))
                    {
                        collide = true;
                        break;
                    }
                }//end collide for
                if (!collide) dungeon.rooms.Add(tempRoom);
            }  //end while

            dijMap = new Dijkstra2DMap();
            dijMap.createMap(dungeon.dungeonWidth, dungeon.dungeonHeight, 999);

            //fill in room with tile 3 and border
            //also set blocked in dij
            for (int i = 0; i < dungeon.rooms.Count; i++)
            {
                dungeon.rooms[i].GenerateBorderIndex(dungeon.dungeonWidth, dungeon.dungeonHeight);
                int[] list = dungeon.rooms[i].GetBorderIndex();
                foreach (int curWall in list)
                {
                    //dungeon.tileMap[curWall] = rnd.Next(3,7);
                    dijMap.SetBlocked(curWall);
                }

                for (int row = dungeon.rooms[i].top; row < dungeon.rooms[i].bottom; row++)
                {
                    for (int col = dungeon.rooms[i].area.Left; col < dungeon.rooms[i].right; col++)
                    {
                        //values here assume using the included tilemap
                        int index = row * dungeon.dungeonWidth + col;
                        int roll = rnd.Next(0, 100);
                        if (roll < 5) dungeon.tileMap[index] = 6; //blood
                        else if (roll < 15) dungeon.tileMap[index] = 5; //water
                        else if (roll < 50) dungeon.tileMap[index] = 4; //mold
                        else dungeon.tileMap[index] = 3;  //normal
					
                    }//end col for
                }//end row for
            }//end room for
			
			
			
            //update blocked indicies
            dijMap.ResetMap();

            //make corridors
            CreateCorridors(dungeon, rnd);
            //CreateCorridors(dungeon, rnd);

            //dungeon.log.WriteDebug("It took " + count + " tries to build");

            //EqatecMonitor.StopFeatureTime("Rogue.FourRoomDungeonGenerate");
            byte[] data = BitConverter.GetBytes(count);
            //EqatecMonitor.SendFeatureData("Rogue.FourRoomFailCount", count);

        }

        private void CreateCorridors(Dungeon dungeon, Random rnd)
        {
            for (int i = 0; i < dungeon.rooms.Count-1; i++)
            {
                int startIndex;
                int stopIndex;

                int startX, startY, stopX, stopY;

                startX = rnd.Next(dungeon.rooms[i].left, dungeon.rooms[i].right);
                stopX = rnd.Next(dungeon.rooms[i + 1].left, dungeon.rooms[i + 1].right);

                startY = rnd.Next(dungeon.rooms[i].top, dungeon.rooms[i].bottom);
                stopY = rnd.Next(dungeon.rooms[i + 1].top, dungeon.rooms[i + 1].bottom);

                //now, which side to move on for start?
                switch (rnd.Next(0, 4))
                //switch(3)
                {
                    case 0:
                        //north
                        startY = dungeon.rooms[i].top - 1;
                        break;
                    case 1:
                        //east
                        startX = dungeon.rooms[i].right;
                        break;
                    case 2:
                        //south
                        startY = dungeon.rooms[i].bottom;
                        break;
                    case 3:
                        //west
                        startX = dungeon.rooms[i].left - 1;
                        break;
                }

                //now, which side to move on for stop?
                switch (rnd.Next(0, 4))
                //switch(3)
                {
                    case 0:
                        //north
                        stopY = dungeon.rooms[i + 1].top - 1;
                        break;
                    case 1:
                        //east
                        stopX = dungeon.rooms[i + 1].right;
                        break;
                    case 2:
                        //south
                        stopY = dungeon.rooms[i + 1].bottom;
                        break;
                    case 3:
                        //west
                        stopX = dungeon.rooms[i + 1].left - 1;
                        break;
                }

                startIndex = startY * dungeon.dungeonWidth + startX;
                stopIndex = stopY * dungeon.dungeonWidth + stopX;

                //dijMap.blockedIndices.Remove(stopIndex);
                //dijMap.blockedIndices.Remove(startIndex);
                //dijMap.SetGoal(stopIndex);
                dijMap.ResetMap();
                //dijMap.LogDijMap();

                dijMap.djMap[stopIndex] = dijMap.defaultValue;
                dijMap.djMap[startIndex] = dijMap.defaultValue;

                //dijMap.djMap[stopIndex] = 57575757;
                //dijMap.djMap[startIndex] = 34343434;

                //dijMap.LogDijMap();

                dijMap.djMap[stopIndex] = 0;
                dijMap.runDij(stopIndex);
                //dijMap.LogDijMap();

                DoCorridor(startIndex);
            }

            //corriders are done, so mark the walkable areas
            foreach (Corridor cur in dungeon.corridors)
            {
                for (int i = 0; i < cur.values.Count; i++) dungeon.tileMap[cur.values[i]] = 3;

            }
        }

        private void DoCorridor(int startIndex)
        {
            Corridor cur = new Corridor();
            dungeon.corridors.Add(cur);
            //cur.values.Add(startIndex);
            int curIndex = startIndex;
            //dungeon.tileMap[curIndex] = 2;


            while (dijMap.djMap[curIndex] != 0)
            {
                cur.values.Add(curIndex);
                //find smallest
                int targetValue = 0;
                int targetIndex = 0;
                int smallest = dijMap.defaultValue;

                int x = curIndex % dungeon.dungeonWidth;
                int y = curIndex / dungeon.dungeonWidth;
                
                if (x - 1 >= 0)
                {
                    targetIndex = y * dungeon.dungeonWidth + x - 1;
                    if (smallest > dijMap.djMap[targetIndex])
                    {
                        targetValue = targetIndex;
                        smallest = dijMap.djMap[targetIndex];
                    }
                }

                if (x + 1 < dungeon.dungeonWidth)
                {
                    targetIndex = y * dungeon.dungeonWidth + x + 1;
                    if (smallest > dijMap.djMap[targetIndex])
                    {
                        targetValue = targetIndex;
                        smallest = dijMap.djMap[targetIndex];
                    }
                }

                if (y - 1 >= 0)
                {
                    targetIndex = (y - 1) * dungeon.dungeonWidth + x;
                    if (smallest > dijMap.djMap[targetIndex])
                    {
                        targetValue = targetIndex;
                        smallest = dijMap.djMap[targetIndex];
                    }
                }

                if (y + 1 < dungeon.dungeonHeight)
                {
                    targetIndex = (y + 1) * dungeon.dungeonWidth + x;
                    if (smallest > dijMap.djMap[targetIndex])
                    {
                        targetValue = targetIndex;
                        smallest = dijMap.djMap[targetIndex];
                    }
                }

                curIndex = targetValue;
            }

            //add in the stop point;
            cur.values.Add(curIndex);
            
        }
	
    }


